/*
 Copyright (C) 2007 - Michael Opitz
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "my-ssl.h"

static int init = 0;

SSL_CTX* 
init_ctx ()
{
	/* we are obtaining here a new ssl-context */
	SSL_METHOD *method;
	SSL_CTX *ctx;
	OpenSSL_add_all_algorithms ();
	SSL_load_error_strings ();
	method = TLSv1_client_method ();
	ctx = SSL_CTX_new (method);
	if (ctx == NULL) {
		ERR_print_errors_fp (stderr);
		return NULL;
	}
	return ctx;
}

ssl_system* init_ssl ()
{
	/* SSL_library_init should only called once */
	if (init == 0) {
		init = 1;
		SSL_library_init ();
	} else {
		init = init + 1;
	}
	ssl_system *sys = (ssl_system*) malloc (sizeof (ssl_system));
	sys->_ctx = init_ctx ();
	if (sys->_ctx == NULL) {
		ERR_remove_state (0);
		ERR_free_strings ();
		EVP_cleanup ();
		free (sys);
		return NULL;
	}
	return sys;
}

void 
free_ssl (ssl_system *sys)
{
	/* cleanup the ctx + the system */
	assert (sys != NULL);
	SSL_CTX_free (sys->_ctx);
	ERR_remove_state (0);
	ERR_free_strings ();
	EVP_cleanup ();
	free (sys);
}

void 
close_socket (ssl_system *sys, ssl_socket *sock)
{
	assert (sys != NULL && sock != NULL);
	SSL_free (sock->_ssl);
	close (sock->_sock);
	free (sock);
}

ssl_socket* 
open_socket (ssl_system *sys, const char *hostname, int port)
{
	assert (sys != NULL);

	SSL *ssl;
	int sd;
	struct hostent *host;
	struct sockaddr_in addr;
	/* resolving hostname */
	if ((host = gethostbyname(hostname)) == NULL) {
		perror (hostname);
		return NULL;
	}
	/* connecting to the IP */
	sd = socket (PF_INET, SOCK_STREAM, 0);
	bzero (&addr, sizeof (addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons (port);
	addr.sin_addr.s_addr = *(long*) (host->h_addr);
	if (connect (sd, (struct sockaddr*)&addr, sizeof (addr)) != 0) {
		close (sd);
		perror (hostname);
		return NULL;
	}

	/* establishing ssl over the socket */
	ssl = SSL_new (sys->_ctx);
	SSL_set_fd (ssl, sd);
	if (SSL_connect (ssl) == -1) {
		ERR_print_errors_fp (stderr);
		close (sd);
		return NULL;
	}
	
	/* filling our internal structure with the data */
	ssl_socket *sock = (ssl_socket*) malloc (sizeof(ssl_socket));
	sock->_sock = sd;
	sock->_ssl = ssl;
	return sock;
}

void 
write_sock (ssl_system *sys, ssl_socket *sock, const char *msg)
{
	assert (sys != NULL && sock != NULL);
	SSL_write (sock->_ssl, msg, strlen(msg));
}

int 
read_sock (ssl_system *sys, ssl_socket *sock, char *buff, int size)
{
	assert (sys != NULL && sock != NULL);
	return SSL_read (sock->_ssl, buff, size);
}

